package org.xteam.box2text;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class Text {
	
	private List<Line> lines;
	
	public Text(List<Line> lines) {
		this.lines = lines;
	}
	
	public Text() {
		this(new ArrayList<Line>());
	}

	/**
	 * Concatenate vertically the two texts.
	 *  
	 * a* // b* = a* b*
	 * 
	 * @param other
	 * @return
	 */
	public Text verticalConcat(Text other) {
		List<Line> l = new ArrayList<Line>(lines);
		l.addAll(other.lines);
		return new Text(l);
	}
	
	public Text leftVerticalConcat(Text other) {
		if (lines.isEmpty())
			return this;
		return verticalConcat(other);
	}
	
	public Text rightVerticalConcat(Text other) {
		if (other.lines.isEmpty())
			return other;
		return verticalConcat(other);
	}
	
	/**
	 * Indent <code>other</code> text by the size of the last line
	 * of text. the last line of text and the first line of other text are concatenated.
	 * 
	 *      || T = T
	 * a* a || T = a* // a | T
	 *           = new Text(a*).verticalConcat(firstPrefix(a))
	 */
	public Text indentConcat(Text other) {
		if (lines.isEmpty())
			return other;
		List<Line> l = new ArrayList<Line>();
		for (int i = 0; i < lines.size()-1; ++i) {
			l.add(lines.get(i));
		}
		l.addAll(other.firstPrefix(lines.get(lines.size()-1)).lines);
		return new Text(l);
	}
	
	public Text leftIndentConcat(Text other) {
		if (lines.isEmpty())
			return this;
		return indentConcat(other);
	}
	
	public Text rightIndentConcat(Text other) {
		if (other.lines.isEmpty())
			return other;
		return indentConcat(other);
	}
	
	/**
	 * Prefix the first line of the text with line <code>a</code>, the others lines
	 * will be prefixed with blanks of the same width as <code>a</code>.
	 * 
	 * a |      = a
	 * a | b b* = a * b // blank(a) -| b*
	 *          = verticalConcat(new Text(a.concat(head())), tail().prefix(a.blank()))
	 */
	public Text firstPrefix(Line a) {
		List<Line> l = new ArrayList<Line>();
		if (lines.isEmpty()) {
			l.add(a);
			return new Text(l);
		}
		boolean isFirst = true;
		Line blank = a.blank();
		for (Line line : lines) {
			if (isFirst) {
				l.add(a.concat(line));
				isFirst = false;
			} else {
				l.add(blank.concat(line));
			}
		}
		return new Text(l);
	}
	
	/**
	 * Prefix every lines of the text with Line <code>a</code>.
	 * 
	 * a -|      =
	 * a -| b b* = a * b // a -| b* = new Text(a.concat(head())).verticalConcat(tail().prefix(a))
	 */
	public Text prefix(Line a) {
		List<Line> l = new ArrayList<Line>();
		for (Line line : lines) {
			l.add(a.concat(line));
		}
		return new Text(l);
	}
	
	/**
	 * Maximum width of the text
	 * 
	 * @return
	 */
	public int twidth() {
		int max = 0;
		for (Line line : lines) {
			if (line.width() > max)
				max = line.width();
		}
		return max;
	}
	
	/**
	 * Width of the last line.
	 * 
	 * @return
	 */
	public int hwidth() {
		return lines.isEmpty() ? 0 : lines.get(lines.size()-1).width();
	}
	
	/**
	 * Create a blank line of the same width of the last line.
	 * 
	 * @return
	 */
	public Text wd() {
		return hskip(hwidth());
	}
	
	/**
	 * Create a text which a horizontal space of width <code>s</code>.
	 * 
	 * @param s
	 * @return
	 */
	public static Text hskip(int s) {
		char[] data = new char[s];
		Arrays.fill(data, ' ');
		List<Line> l = new ArrayList<Line>();
		l.add(new Line(new String(data)));
		return new Text(l);
	}
	
	/**
	 * Create a text which a vertical space of width <code>s</code>.
	 * 
	 * @param s
	 * @return
	 */
	public static Text vskip(int s) {
		List<Line> l = new ArrayList<Line>();
		for (int i = 0; i < s; ++i) {
			l.add(new Line(""));
		}
		return new Text(l);
	}
	
	@Override
	public String toString() {
		StringBuffer buffer = new StringBuffer();
		for (Line line : lines) {
			buffer.append(line);
			buffer.append('\n');
		}
		return buffer.toString();
	}

	public Text addToLastLine(Line other) {
		List<Line> l = new ArrayList<Line>();
		if (lines.isEmpty()) {
			l.add(other);
		} else {
			for (int i = 0; i < lines.size() - 1; ++i) {
				l.add(lines.get(i));
			}
			l.add(lines.get(lines.size()-1).concat(other));
		}
		return new Text(l);
	}

	public Text startNewLine() {
		List<Line> l = new ArrayList<Line>(lines);
		l.add(new Line(""));
		return new Text(l);
	}
	
	public Text alignLeft(int rightMargin) {
		List<Line> l = new ArrayList<Line>();
		for (Line line : lines) {
			l.add(line.concat(new Line(rightMargin - line.width())));
		}
		return new Text(l);
	}

	public Text alignRight(int rightMargin) {
		List<Line> l = new ArrayList<Line>();
		for (Line line : lines) {
			l.add(new Line(rightMargin - line.width()).concat(line));
		}
		return new Text(l);
	}

	public Text alignCenter(int rightMargin) {
		List<Line> l = new ArrayList<Line>();
		for (Line line : lines) {
			int availableSpace = rightMargin - line.width();
			int leftSpace = availableSpace / 2;
			int rightSpace = leftSpace * 2 < availableSpace ? leftSpace + 1 : leftSpace;
			l.add(new Line(leftSpace).concat(line).concat(new Line(rightSpace)));
		}
		return new Text(l);
	}
}
